In [1]:
import tensorflow as tf
from tensorflow.keras import models, layers
import matplotlib.pyplot as plt
In [2]:
BATCH_SIZE = 32
IMAGE_SIZE = 256
CHANNELS=3
EPOCHS=50
In [3]:
dataset = tf.keras.preprocessing.image_dataset_from_directory(
"PlantVillage",
seed=123,
shuffle=True,
image_size=(IMAGE_SIZE,IMAGE_SIZE),
batch_size=BATCH_SIZE
)
Found 2152 files belonging to 3 classes.
In [4]:
class_names = dataset.class_names
class_names
Out[4]:
['Potato___Early_blight', 'Potato___Late_blight', 'Potato___healthy']
In [5]:
len(dataset)
Out[5]:
68
In [6]:
for image_batch, labels_batch in dataset.take(1):
print(image_batch.shape)
print(labels_batch.numpy())
(32, 256, 256, 3) [1 1 1 0 0 0 0 0 1 1 1 1 0 1 0 1 1 1 0 1 0 1 0 0 1 0 0 1 1 2 0 0]
In [7]:
plt.figure(figsize=(10, 10))
for image_batch, labels_batch in dataset.take(1):
for i in range(12):
ax = plt.subplot(3, 4, i + 1)
plt.imshow(image_batch[i].numpy().astype("uint8"))
plt.title(class_names[labels_batch[i]])
plt.axis("off")
In [8]:
len(dataset)
Out[8]:
68
In [9]:
train_size = 0.8
len(dataset)*train_size
Out[9]:
54.400000000000006
In [10]:
train_ds = dataset.take(54)
len(train_ds)
Out[10]:
54
In [11]:
test_ds = dataset.skip(54)
len(test_ds)
Out[11]:
14
In [12]:
val_size=0.1
len(dataset)*val_size
Out[12]:
6.800000000000001
In [13]:
val_ds = test_ds.take(6)
len(val_ds)
Out[13]:
6
In [14]:
test_ds = test_ds.skip(6)
len(test_ds)
Out[14]:
8
In [15]:
def get_dataset_partitions_tf(ds, train_split=0.8, val_split=0.1, test_split=0.1, shuffle=True, shuffle_size=10000):
assert (train_split + test_split + val_split) == 1
ds_size = len(ds)
if shuffle:
ds = ds.shuffle(shuffle_size, seed=12)
train_size = int(train_split * ds_size)
val_size = int(val_split * ds_size)
train_ds = ds.take(train_size)
val_ds = ds.skip(train_size).take(val_size)
test_ds = ds.skip(train_size).skip(val_size)
return train_ds, val_ds, test_ds
In [16]:
train_ds, val_ds, test_ds = get_dataset_partitions_tf(dataset)
In [17]:
len(train_ds)
Out[17]:
54
In [18]:
len(val_ds)
Out[18]:
6
In [19]:
len(test_ds)
Out[19]:
8
In [20]:
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=tf.data.AUTOTUNE)
val_ds = val_ds.cache().shuffle(1000).prefetch(buffer_size=tf.data.AUTOTUNE)
test_ds = test_ds.cache().shuffle(1000).prefetch(buffer_size=tf.data.AUTOTUNE)
In [21]:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
In [22]:
IMAGE_SIZE = 256 # Change this to your desired image size
resize_and_rescale = keras.Sequential([
layers.Resizing(IMAGE_SIZE, IMAGE_SIZE), # No "experimental.preprocessing"
layers.Rescaling(1./255),
])
In [23]:
print(tf.__version__)
2.18.0
In [24]:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
IMAGE_SIZE = 224 # Adjust as needed
resize_and_rescale = keras.Sequential([
layers.Resizing(IMAGE_SIZE, IMAGE_SIZE), # Correct usage
layers.Rescaling(1./255), # Correct usage
])
In [25]:
data_augmentation = keras.Sequential([
layers.RandomFlip("horizontal_and_vertical"), # Corrected
layers.RandomRotation(0.2), # Corrected
])
In [26]:
train_ds = train_ds.map(
lambda x, y: (data_augmentation(x, training=True), y)
).prefetch(buffer_size=tf.data.AUTOTUNE)
In [27]:
input_shape = (BATCH_SIZE, IMAGE_SIZE, IMAGE_SIZE, CHANNELS)
n_classes = 3
model = models.Sequential([
resize_and_rescale,
layers.Conv2D(32, kernel_size = (3,3), activation='relu', input_shape=input_shape),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, kernel_size = (3,3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, kernel_size = (3,3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Flatten(),
layers.Dense(64, activation='relu'),
layers.Dense(n_classes, activation='softmax'),
])
C:\Users\Shravani\anaconda3\SHRAVANI 01\Lib\site-packages\keras\src\layers\convolutional\base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead. super().__init__(activity_regularizer=activity_regularizer, **kwargs)
In [28]:
model.build(input_shape=input_shape)
In [29]:
model.summary()
Model: "sequential_3"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ sequential_1 (Sequential) │ (32, 224, 224, 3) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d (Conv2D) │ (32, 222, 222, 32) │ 896 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d (MaxPooling2D) │ (32, 111, 111, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_1 (Conv2D) │ (32, 109, 109, 64) │ 18,496 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (32, 54, 54, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_2 (Conv2D) │ (32, 52, 52, 64) │ 36,928 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (32, 26, 26, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_3 (Conv2D) │ (32, 24, 24, 64) │ 36,928 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_3 (MaxPooling2D) │ (32, 12, 12, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_4 (Conv2D) │ (32, 10, 10, 64) │ 36,928 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_4 (MaxPooling2D) │ (32, 5, 5, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_5 (Conv2D) │ (32, 3, 3, 64) │ 36,928 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ max_pooling2d_5 (MaxPooling2D) │ (32, 1, 1, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (32, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (32, 64) │ 4,160 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (32, 3) │ 195 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 171,459 (669.76 KB)
Trainable params: 171,459 (669.76 KB)
Non-trainable params: 0 (0.00 B)
In [30]:
model.compile(
optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['accuracy']
)
In [31]:
history = model.fit(
train_ds,
batch_size=BATCH_SIZE,
validation_data=val_ds,
verbose=1,
epochs=50,
)
Epoch 1/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 81s 1s/step - accuracy: 0.4803 - loss: 0.9428 - val_accuracy: 0.4375 - val_loss: 0.8926 Epoch 2/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.5227 - loss: 0.8748 - val_accuracy: 0.7292 - val_loss: 0.6152 Epoch 3/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 71s 1s/step - accuracy: 0.7511 - loss: 0.6128 - val_accuracy: 0.6354 - val_loss: 0.7688 Epoch 4/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 71s 1s/step - accuracy: 0.8048 - loss: 0.5317 - val_accuracy: 0.8177 - val_loss: 0.4932 Epoch 5/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.7905 - loss: 0.5145 - val_accuracy: 0.8177 - val_loss: 0.4321 Epoch 6/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.8424 - loss: 0.3795 - val_accuracy: 0.7865 - val_loss: 0.4805 Epoch 7/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.8375 - loss: 0.3972 - val_accuracy: 0.8802 - val_loss: 0.3073 Epoch 8/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.8710 - loss: 0.3198 - val_accuracy: 0.9010 - val_loss: 0.2190 Epoch 9/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9134 - loss: 0.2114 - val_accuracy: 0.8958 - val_loss: 0.2537 Epoch 10/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 82s 1s/step - accuracy: 0.9435 - loss: 0.1567 - val_accuracy: 0.9583 - val_loss: 0.1455 Epoch 11/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 84s 1s/step - accuracy: 0.9584 - loss: 0.1159 - val_accuracy: 0.8698 - val_loss: 0.3473 Epoch 12/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9154 - loss: 0.2271 - val_accuracy: 0.9375 - val_loss: 0.1428 Epoch 13/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 83s 1s/step - accuracy: 0.9752 - loss: 0.0701 - val_accuracy: 0.9792 - val_loss: 0.0969 Epoch 14/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.9774 - loss: 0.0735 - val_accuracy: 0.9792 - val_loss: 0.0658 Epoch 15/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9767 - loss: 0.0735 - val_accuracy: 0.9271 - val_loss: 0.2017 Epoch 16/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 82s 1s/step - accuracy: 0.9699 - loss: 0.0833 - val_accuracy: 0.9635 - val_loss: 0.1222 Epoch 17/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 82s 1s/step - accuracy: 0.9677 - loss: 0.0952 - val_accuracy: 0.9531 - val_loss: 0.1249 Epoch 18/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 82s 1s/step - accuracy: 0.9806 - loss: 0.0551 - val_accuracy: 0.9740 - val_loss: 0.0709 Epoch 19/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.9850 - loss: 0.0403 - val_accuracy: 0.9896 - val_loss: 0.0311 Epoch 20/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.9861 - loss: 0.0413 - val_accuracy: 0.9844 - val_loss: 0.0708 Epoch 21/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 82s 1s/step - accuracy: 0.9688 - loss: 0.0804 - val_accuracy: 0.9583 - val_loss: 0.1019 Epoch 22/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9824 - loss: 0.0527 - val_accuracy: 0.9844 - val_loss: 0.0591 Epoch 23/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9796 - loss: 0.0755 - val_accuracy: 0.9896 - val_loss: 0.0348 Epoch 24/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 83s 1s/step - accuracy: 0.9800 - loss: 0.0606 - val_accuracy: 0.9844 - val_loss: 0.0410 Epoch 25/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9892 - loss: 0.0395 - val_accuracy: 0.9896 - val_loss: 0.0271 Epoch 26/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9927 - loss: 0.0272 - val_accuracy: 0.9792 - val_loss: 0.0393 Epoch 27/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9807 - loss: 0.0444 - val_accuracy: 0.9844 - val_loss: 0.0444 Epoch 28/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 83s 1s/step - accuracy: 0.9880 - loss: 0.0296 - val_accuracy: 0.9948 - val_loss: 0.0140 Epoch 29/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9812 - loss: 0.0619 - val_accuracy: 0.9219 - val_loss: 0.1288 Epoch 30/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9828 - loss: 0.0475 - val_accuracy: 0.9948 - val_loss: 0.0231 Epoch 31/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 83s 1s/step - accuracy: 0.9845 - loss: 0.0345 - val_accuracy: 0.9896 - val_loss: 0.0241 Epoch 32/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 82s 1s/step - accuracy: 0.9954 - loss: 0.0173 - val_accuracy: 0.9688 - val_loss: 0.0833 Epoch 33/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9804 - loss: 0.0632 - val_accuracy: 0.9844 - val_loss: 0.0380 Epoch 34/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 83s 1s/step - accuracy: 0.9815 - loss: 0.0463 - val_accuracy: 0.9948 - val_loss: 0.0266 Epoch 35/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9877 - loss: 0.0406 - val_accuracy: 1.0000 - val_loss: 0.0193 Epoch 36/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9925 - loss: 0.0253 - val_accuracy: 0.9844 - val_loss: 0.0309 Epoch 37/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.9903 - loss: 0.0211 - val_accuracy: 0.9167 - val_loss: 0.2392 Epoch 38/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.9819 - loss: 0.0488 - val_accuracy: 0.9844 - val_loss: 0.0400 Epoch 39/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 82s 1s/step - accuracy: 0.9939 - loss: 0.0184 - val_accuracy: 0.9792 - val_loss: 0.0778 Epoch 40/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9912 - loss: 0.0268 - val_accuracy: 0.9896 - val_loss: 0.0302 Epoch 41/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9931 - loss: 0.0162 - val_accuracy: 0.9948 - val_loss: 0.0095 Epoch 42/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.9866 - loss: 0.0401 - val_accuracy: 1.0000 - val_loss: 0.0049 Epoch 43/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9897 - loss: 0.0293 - val_accuracy: 0.9948 - val_loss: 0.0100 Epoch 44/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 69s 1s/step - accuracy: 0.9984 - loss: 0.0097 - val_accuracy: 0.9844 - val_loss: 0.0423 Epoch 45/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 83s 1s/step - accuracy: 0.9902 - loss: 0.0242 - val_accuracy: 0.9792 - val_loss: 0.0714 Epoch 46/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 71s 1s/step - accuracy: 0.9930 - loss: 0.0173 - val_accuracy: 0.9844 - val_loss: 0.0276 Epoch 47/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 70s 1s/step - accuracy: 0.9941 - loss: 0.0205 - val_accuracy: 0.9792 - val_loss: 0.0497 Epoch 48/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 71s 1s/step - accuracy: 0.9843 - loss: 0.0551 - val_accuracy: 0.9844 - val_loss: 0.0322 Epoch 49/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 72s 1s/step - accuracy: 0.9964 - loss: 0.0135 - val_accuracy: 0.9792 - val_loss: 0.0419 Epoch 50/50 54/54 ━━━━━━━━━━━━━━━━━━━━ 71s 1s/step - accuracy: 0.9816 - loss: 0.0531 - val_accuracy: 0.9323 - val_loss: 0.1460
In [50]:
scores = model.evaluate(test_ds)
8/8 ━━━━━━━━━━━━━━━━━━━━ 3s 320ms/step - accuracy: 0.9499 - loss: 0.1632
In [51]:
scores
Out[51]:
[0.165579155087471, 0.953125]
In [52]:
history
Out[52]:
<keras.src.callbacks.history.History at 0x1aad2f75130>
In [53]:
history.params
Out[53]:
{'verbose': 1, 'epochs': 50, 'steps': 54}
In [54]:
history.history.keys()
Out[54]:
dict_keys(['accuracy', 'loss', 'val_accuracy', 'val_loss'])
In [55]:
type(history.history['loss'])
Out[55]:
list
In [56]:
len(history.history['loss'])
Out[56]:
50
In [57]:
history.history['loss'][:5] # show loss for first 5 epochs
Out[57]:
[0.9157429933547974, 0.8123927712440491, 0.5875206589698792, 0.5011534690856934, 0.499131441116333]
In [58]:
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
In [59]:
plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(range(EPOCHS), acc, label='Training Accuracy')
plt.plot(range(EPOCHS), val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
Out[59]:
Text(0.5, 1.0, 'Training and Validation Accuracy')
In [60]:
plt.subplot(1, 2, 2)
plt.plot(range(EPOCHS), loss, label='Training Loss')
plt.plot(range(EPOCHS), val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
In [61]:
#Run prediction on a sample image
In [62]:
import numpy as np
for images_batch, labels_batch in test_ds.take(1):
first_image = images_batch[0].numpy().astype('uint8')
first_label = labels_batch[0].numpy()
print("first image to predict")
plt.imshow(first_image)
print("actual label:",class_names[first_label])
batch_prediction = model.predict(images_batch)
print("predicted label:",class_names[np.argmax(batch_prediction[0])])
first image to predict actual label: Potato___Late_blight 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 383ms/step predicted label: Potato___Late_blight
In [63]:
import numpy as np
for images_batch, labels_batch in test_ds.take(1):
first_image = images_batch[0].numpy().astype('uint8')
first_label = labels_batch[0].numpy()
print("first image to predict")
plt.imshow(first_image)
print("actual label:",class_names[first_label])
batch_prediction = model.predict(images_batch)
print("predicted label:",class_names[np.argmax(batch_prediction[0])])
first image to predict actual label: Potato___Early_blight 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 408ms/step predicted label: Potato___Early_blight
In [64]:
def predict(model, img):
img_array = tf.keras.preprocessing.image.img_to_array(images[i].numpy())
img_array = tf.expand_dims(img_array, 0)
predictions = model.predict(img_array)
predicted_class = class_names[np.argmax(predictions[0])]
confidence = round(100 * (np.max(predictions[0])), 2)
return predicted_class, confidence
In [65]:
plt.figure(figsize=(15, 15))
for images, labels in test_ds.take(1):
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
predicted_class, confidence = predict(model, images[i].numpy())
actual_class = class_names[labels[i]]
plt.title(f"Actual: {actual_class},\n Predicted: {predicted_class}.\n Confidence: {confidence}%")
plt.axis("off")
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 103ms/step 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 95ms/step 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 93ms/step 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 127ms/step 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 112ms/step 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 89ms/step 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 120ms/step 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 111ms/step 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 87ms/step
In [66]:
import os
models_dir = "../models"
# Ensure the directory exists
os.makedirs(models_dir, exist_ok=True)
# Get the highest existing model version
model_version = max([int(i) for i in os.listdir(models_dir) if i.isdigit()] + [0]) + 1
# Save the model with a valid extension
model.save(f"{models_dir}/{model_version}.keras")
In [67]:
model.save("../potatoes.keras")
In [ ]: